package com.moonLight.weblog.web.service.Impl;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.moonLight.weblog.common.domain.dos.BlogSettingsDO;
import com.moonLight.weblog.common.domain.dos.CommentDO;
import com.moonLight.weblog.common.domain.mapper.BlogSettingsMapper;
import com.moonLight.weblog.common.domain.mapper.CommentMapper;
import com.moonLight.weblog.common.enums.CommentStatusEnum;
import com.moonLight.weblog.common.enums.ResponseCodeEnum;
import com.moonLight.weblog.common.exception.BizException;
import com.moonLight.weblog.common.utils.Response;
import com.moonLight.weblog.web.convert.CommentConvert;
import com.moonLight.weblog.web.event.PublishCommentEvent;
import com.moonLight.weblog.web.model.vo.comment.*;
import com.moonLight.weblog.web.service.CommentService;
import com.moonLight.weblog.web.utils.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import toolgood.words.IllegalWordsSearch;
import toolgood.words.IllegalWordsSearchResult;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class CommentServiceImpl implements CommentService {
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private BlogSettingsMapper blogSettingsMapper;
    @Autowired
    private CommentMapper commentMapper;
    @Autowired
    private IllegalWordsSearch wordsSearch;

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;
    @Override
    public Response findQQUserInfo(FindQQUserInfoReqVO findQQUserInfoReqVO) {
        String qq = findQQUserInfoReqVO.getQq();

        // 校验 QQ 号
        if (!StringUtil.isPureNumber(qq)) {
            log.warn("昵称输入的格式不是 QQ 号: {}", qq);
            throw new BizException(ResponseCodeEnum.NOT_QQ_NUMBER);
        }

        // 请求第三方接口
        String url = String.format("https://api.qjqq.cn/api/qqinfo?qq=%s", qq);
        String result = restTemplate.getForObject(url, String.class);

        log.info("通过 QQ 号获取用户信息: {}", result);

        // 解析响参
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            // 解析响应结果 将String 转成Map
            Map<String, Object> map = objectMapper.readValue(result, Map.class);
            if (Objects.equals(map.get("code"), HttpStatus.OK.value())) {  //返回是否是200  (成功与否)
                // 获取用户头像、昵称、邮箱
                return Response.success(FindQQUserInfoRspVO.builder()
                        .avatar(String.valueOf(map.get("imgurl")))
                        .nickname(String.valueOf(map.get("name")))
                        .mail(String.valueOf(map.get("mail")))
                        .build());
            }

            return Response.fail();
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 发布评论的接口 实现
     *  第一  第二 需要拿到博客设置表中的信息
     *  1. 判断是否开启评论审核功能
     *      1. 需要博主审核通过后才会发布
     *      2. 第三点的状态需要设置成待审核
     *  2. 判断是否开启敏感词过滤功能
     *      1.存在敏感词则需要过滤敏感词
     *  3. 给评论这是一些默认字段
     *  4.转Do 存数据库
     * @param publishCommentReqVO
     * @return
     */
    @Override
    public Response publishComment(PublishCommentReqVO publishCommentReqVO) {
        // 回复的评论 ID
        Long replyCommentId = publishCommentReqVO.getReplyCommentId();
        // 评论内容
        String content = publishCommentReqVO.getContent();
        // 昵称
        String nickname = publishCommentReqVO.getNickname();
        // 拿到博客设置信息
        BlogSettingsDO blogSettingsDO = blogSettingsMapper.selectById(1L);//约定Id为1L
        // 是否开启评论审核功能
        Boolean isCommentExamineOpen = blogSettingsDO.getIsCommentExamineOpen();
        // 是否开启敏感词过滤功能
        Boolean isCommentSensiWordOpen = blogSettingsDO.getIsCommentSensiWordOpen();
        // 评论的默认状态
        Integer status = CommentStatusEnum.NORMAL.getCode();
        // 审核不通过原因
        String reason = "";
        // 评论内容是否包含敏感词
        boolean isContainSensitiveWord = false;

        // 如果开启了审核, 设置状态为待审核，等待博主后台审核通过
        if (isCommentExamineOpen) {
            status = CommentStatusEnum.WAIT_EXAMINE.getCode();
        }

        // 判断是否开启敏感词过滤功能
        if (isCommentSensiWordOpen){
            // 校验评论中是否包含敏感词
            isContainSensitiveWord = wordsSearch.ContainsAny(content);
            if (isContainSensitiveWord){
                // 存在敏感词
                status = CommentStatusEnum.EXAMINE_FAILED.getCode();
                // 匹配到的所有敏感词组
                List<IllegalWordsSearchResult> results = wordsSearch.FindAll(content);
                List<String> keywords = results.stream().map(item ->item.Keyword).collect(Collectors.toList());
                reason = String.format("系统自动拦截，包含敏感词：%s", keywords);
                log.warn("此评论内容中包含敏感词: {}, content: {}", keywords, content);
            }
        }

        // 构建 DO 对象
        CommentDO commentDO = CommentDO.builder()
                .avatar(publishCommentReqVO.getAvatar())
                .content(content)
                .mail(publishCommentReqVO.getMail())
                .createTime(LocalDateTime.now())
                .nickname(nickname)
                .routerUrl(publishCommentReqVO.getRouterUrl())
                .website(publishCommentReqVO.getWebsite())
                .replyCommentId(replyCommentId)
                .parentCommentId(publishCommentReqVO.getParentCommentId())
                .status(status)
                .reason(reason)
                .build();

        // 新增评论
        commentMapper.insert(commentDO);

        // 发布评论事件 邮箱通知
        Long commentId = commentDO.getId();
        applicationEventPublisher.publishEvent(new PublishCommentEvent(this,commentId,blogSettingsDO));

        //给前端的响应
        if (isContainSensitiveWord){
            // 存在敏感词  抛出业务异常
            throw new BizException(ResponseCodeEnum.COMMENT_CONTAIN_SENSITIVE_WORD);
        }
        if (Objects.equals(status, CommentStatusEnum.WAIT_EXAMINE.getCode())){
            //审核等待
            throw new BizException(ResponseCodeEnum.COMMENT_WAIT_EXAMINE);
        }



        return Response.success();
    }

    @Override
    public Response findCommentList(FindCommentListReqVO findCommentListReqVO) {
        String routerUrl = findCommentListReqVO.getRouterUrl();
        List<CommentDO> commentDOS = commentMapper.selectByRouterUrlAndStatus(routerUrl, CommentStatusEnum.NORMAL.getCode());
        Integer total = commentDOS.size();
        List<FindCommentItemRspVO> vos = new ArrayList<>();
        // 对评论进行整理
        if (!CollectionUtils.isEmpty(commentDOS)){
            /**
             * 1.筛选出一级评论 没有父级评论ID
             * 2. 二级评论分类
             *      按照父级评论ID进行分组
             *      子评论按照升序排序
             *      回复评论的ID不等于一级评论的ID  需要展示[@回复人昵称]
             */
            //拿到一级评论
            vos = commentDOS.stream()
                    .filter(comment -> Objects.isNull(comment.getParentCommentId())) // 一级评论
                    .map(commentDo -> CommentConvert.INSTANCE.convertDO2VO(commentDo))// Do 转 Vo
                    .collect(Collectors.toList());
           vos.forEach(item -> {
               Long id = item.getId();   //拿到一级评论的ID
               List<FindCommentItemRspVO> childCommentDOS = commentDOS.stream()
                       .filter(comment -> Objects.equals(comment.getParentCommentId(), id)) // 二级评论
                       .sorted(Comparator.comparing(CommentDO::getCreateTime)) // 按照创建时间升序排序
                       .map( // 子级评论是否展示 回复人昵称
                               comment -> {
                                   // Do 转 Vo
                                   FindCommentItemRspVO  findCommentItemRspVO = CommentConvert.INSTANCE.convertDO2VO(comment);
                                   Long replyCommentId = comment.getReplyCommentId();
                                   if (!Objects.equals(replyCommentId, id)){
                                       // 回复评论的ID不等于一级评论的ID  需要展示[@回复人昵称]
                                       // 从commentDOS 集合中拿到符合comment.getReplyCommentId()的数据 从中拿到nickname
                                       Optional<CommentDO> optionalCommentDO = commentDOS.stream().filter(
                                               commentItem -> Objects.equals(commentItem.getId(), replyCommentId
                                               )).findFirst();
                                       if (optionalCommentDO.isPresent()){ //如果存在则直接转换
                                           findCommentItemRspVO.setReplyNickname(optionalCommentDO.get().getNickname());
                                       }
                                   }
                                   return findCommentItemRspVO;
                               }
                       ).collect(Collectors.toList());
               item.setChildComments(childCommentDOS);
           });


        }


        return Response.success(FindCommentListRspVO.builder()
                .total(total)
                .comments(vos)
                .build());
    }
}
